#!/usr/bin/env python

import pygtk
# We really require 2.6 but putting 2.6 breaks things
pygtk.require("2.0")
import gtk, pango, gtkspell
import string
import webbrowser

import model
from model import Note, Category, Link

import note_buffer
from note_buffer import NoteBuffer

import dbstrings
import link

from SearchWindow import SearchWindow

class ANTS:
    __wnd__             = gtk.Window(gtk.WINDOW_TOPLEVEL)
    __category_window__ = gtk.ScrolledWindow()
    __category_list__   = gtk.VBox()
    __note_window__     = gtk.Notebook()
    __page_note_map__   = {}
    __calendar__        = None
    __edit_menu__       = gtk.Menu()
    __sw__              = SearchWindow(__note_window__)

    def delete_event(self, widget, event, data=None):
        return False

    def destroy(self, widget, data=None):
        for k in self.__page_note_map__:
            self.quicksave_note(k)
        
        gtk.main_quit()

    # This is disabled for now because it replaces the menu and does not add
    # it. This causes spell check to not show suggestions
    def on_button_press(self, widget, event) :
        if event.button == 3 :
            self.__edit_menu__.popup(None, None, None, event.button, event.time)
            return True

    def cut_selected_text(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            return

        p = self.__note_window__.get_current_page()
        textview = self.__note_window__.get_nth_page(p).get_children()[0]
        textview.get_buffer().cut_text(textview)

    def copy_selected_text(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            self.bold_button.set_active(False)
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        buffer.copy_text()

    def paste_text(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            return

        p = self.__note_window__.get_current_page()
        textview = self.__note_window__.get_nth_page(p).get_children()[0]
        textview.get_buffer().paste_text(textview)

    def delete_selected_text(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            return

        p = self.__note_window__.get_current_page()
        textview = self.__note_window__.get_nth_page(p).get_children()[0]
        textview.get_buffer().delete_text(textview)

    def select_all(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        buffer.select_all()

    def link_site(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            dialog = gtk.Dialog("No Notes Open", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: A Link must be created in an open note.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()

        if buffer.get_selection_bounds() == () :
            dialog = gtk.Dialog("No Selection Made", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: A word or phrase must be selected for linking.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        dialog = gtk.Dialog("Create Web Link", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

        label = gtk.Label("URL:")
        dialog.vbox.pack_start(label, True, True, 0)
        label.show()
        entry = gtk.Entry()
        dialog.vbox.pack_start(entry, True, True, 0)
        entry.show()

        result = dialog.run()
        url = entry.get_text()
        dialog.destroy()

        if result == gtk.RESPONSE_ACCEPT:
            start, end = buffer.get_selection_bounds()
            phrase = buffer.get_text(start, end, False)
            phrase = string.lower(phrase).lstrip().rstrip()

            l = Link(phrase, url)

            n = self.__page_note_map__[p]

            n.links.append(l)
            l.Note = n

            self.apply_link_tag(phrase, buffer)

    def link_wikipedia(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            dialog = gtk.Dialog("No Notes Open", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: A Link must be created in an open note.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        if buffer.get_selection_bounds() == () :
            dialog = gtk.Dialog("No Selection Made", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: A word or phrase must be selected for linking.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        start, end = buffer.get_selection_bounds()
        phrase = buffer.get_text(start, end, False)
        phrase = string.lower(phrase).lstrip().rstrip()

        url = link.get_wiki_destination(phrase)

        if url == 'ERROR':
            dialog = gtk.Dialog("Page Not Found", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("The phrase \"" + phrase + "\" was not found on wikipedia.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        l = Link(phrase, url)
        n = self.__page_note_map__[p]
        n.links.append(l)
        l.Note = n

        self.apply_link_tag(phrase, buffer)


    def link_dictionary(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            dialog = gtk.Dialog("No Notes Open", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: A Link must be created in an open note.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        if buffer.get_selection_bounds() == () :
            dialog = gtk.Dialog("No Selection Made", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: A word or phrase must be selected for linking.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        start, end = buffer.get_selection_bounds()
        phrase = buffer.get_text(start, end, False)
        phrase = string.lower(phrase).lstrip().rstrip()

        url = link.get_dict_destination(phrase)

        if url == 'ERROR':
            dialog = gtk.Dialog("Page Not Found", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("The phrase \"" + phrase + "\" was not found on dictionary.com.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        l = Link(phrase, url)
        n = self.__page_note_map__[p]
        n.links.append(l)
        l.Note = n

        self.apply_link_tag(phrase, buffer)

    def remove_link(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            dialog = gtk.Dialog("No Notes Open", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: A note must be open to remove a link.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        if buffer.get_selection_bounds() == () :
            dialog = gtk.Dialog("No Selection Made", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: A linked phrase must be selected to remove the link.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

        start, end = buffer.get_selection_bounds()
        phrase = buffer.get_text(start, end, False)
        phrase = string.lower(phrase).lstrip().rstrip()

        if (Link.check_phrase_is_linked(phrase)):
            self.remove_link_tag(phrase, buffer)
            Link.get_link_by_phrase(phrase).delete()
        else:
            dialog = gtk.Dialog("Not a Linked Phrase", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

            label = gtk.Label("Error: The selected phrase is not a linked phrase.")
            dialog.vbox.pack_start(label, True, True, 0)
            label.show()

            dialog.run()
            dialog.destroy()
            return

    def refresh_links(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()

        note = self.__page_note_map__[p]
        
        buffer.remove_tag(buffer.link_tag, buffer.get_start_iter(), buffer.get_end_iter())

        for l in note.links:
            self.apply_link_tag(l.phrase, buffer)

    @staticmethod
    def apply_link_tag(phrase, buffer):
        text = string.upper(buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()))
        phrase = string.upper(phrase)
        offset = text.find(phrase)

        while offset != -1:
            start = buffer.get_iter_at_offset(offset)
            end = buffer.get_iter_at_offset(offset + len(phrase))
            buffer.apply_tag(buffer.link_tag, start, end)
            offset = text.find(phrase, offset + len(phrase))

    @staticmethod
    def remove_link_tag(phrase, buffer):
        text = string.upper(buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter()))
        phrase = string.upper(phrase)
        offset = text.find(phrase)

        while offset != -1:
            start = buffer.get_iter_at_offset(offset)
            end = buffer.get_iter_at_offset(offset + len(phrase))
            buffer.remove_tag(buffer.link_tag, start, end)
            offset = text.find(phrase, offset + len(phrase))

    @staticmethod
    def link_handler(tag, textview, event, iter):
        char_index = iter.get_offset()
        tag_name = tag.get_property("name")

        #if event.type == gtk.gdk.MOTION_NOTIFY:
            # change icon to hand?
            #print ''

        if event.type == gtk.gdk.BUTTON_PRESS:
            start = iter.copy()
            end = iter.copy()
            if not start.begins_tag():
                start.backward_to_tag_toggle(tag)
            if not end.ends_tag():
                end.forward_to_tag_toggle(tag)

            buffer = textview.get_buffer()
            phrase = buffer.get_text(start, end, False)
            phrase = string.lower(phrase)

            l = Link.get_link_by_phrase(phrase)

            webbrowser.open_new_tab(l.url)

    def about(self, widget):
        about = gtk.AboutDialog()
        about.set_program_name("Advanced Note Taking System")
        about.set_version("0.1")
        about.set_website("http://code.google.com/p/ants/")
        about.set_license("\
Advanced Note Taking System is free software: you can redistribute \
it and/or modify it under the terms of the GNU General Public \
License as published by the Free Software Foundation, either version \
3 of the License, or (at your option) any later version.\
\
Advanced Note Taking System is distributed in the hope that it will be useful, \
but WITHOUT ANY WARRANTY; without even the implied warranty of \
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the \
GNU General Public License for more details.\
\
You should have received a copy of the GNU General Public License \
along with Foobar.  If not, see <http://www.gnu.org/licenses/>.")
        about.set_wrap_license(True)
        authors = []
        authors.append("Lee Trager <lt73@cs.drexel.edu>")
        authors.append("Matthew Tornetta <mst42@drexel.edu>")
        authors.append("Dan Zollers <djz27@drexel.edu>")
        authors.append("Andrew Orner <aso23@drexel.edu>")
        about.set_authors(authors)
        about.run()
        about.destroy()

    def import_note(self, widget):
        opendialog = gtk.FileChooserDialog("Import text file",
                                     self.__wnd__,
                                     gtk.FILE_CHOOSER_ACTION_OPEN,
                                     (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                      gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        filter = gtk.FileFilter()
        filter.set_name("Text Files")
        filter.add_mime_type("text/plain")
        opendialog.add_filter(filter)

        ret = opendialog.run()
        if ret == gtk.RESPONSE_OK:
            filename = opendialog.get_filename()
            f = open(filename, 'r')
            contents = f.read()

            selCat = self.get_selected_category()
            cat = "Unfiled"

            if selCat != None:
                cat = selCat.get_label()

            n = Note()
            Category.get_category(cat).notes.append(n)
            model.persist()
            n.content = contents

            page_name = gtk.Label("Note " + str(self.__note_window__.get_n_pages() + 1))
            page_name.show()
            window = gtk.ScrolledWindow()
            window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
            buffer = NoteBuffer()
            buffer.note_id = n.id
            buffer.link_tag.connect('event', ANTS.link_handler)
            textview = gtk.TextView(buffer)
            textview.set_wrap_mode(gtk.WRAP_WORD)
            textview.add_events(gtk.gdk.BUTTON_PRESS_MASK)
            textview.connect_after('populate-popup', self.populate_popup_for_links)
            # textview.connect('button_press_event', self.on_button_press)
            window.add(textview)
            window.show()
            textview.show()
            pid = self.__note_window__.append_page(window, page_name)
            self.__page_note_map__[pid] = n

            buffer = textview.get_buffer()
            buffer.set_text(n.content)

        opendialog.destroy()

    def export_note(self, widget):
        export = gtk.FileChooserDialog("Export text file",
                                     self.__wnd__,
                                     gtk.FILE_CHOOSER_ACTION_SAVE,
                                     (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                      gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        export.set_do_overwrite_confirmation(True)
        filter = gtk.FileFilter()
        filter.set_name("Text Files")
        filter.add_mime_type("text/plain")
        export.add_filter(filter)

        ret = export.run()
        if ret == gtk.RESPONSE_OK:
            filename = export.get_filename()
            f = open(filename, 'w')

            p = self.__note_window__.get_current_page()
            buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
            start, end = buffer.get_bounds()

            f.write(buffer.get_text(start, end, False))

        export.destroy()

    def new_note(self, widget):
        selCat = self.get_selected_category()
        cat = "Unfiled"

        dialog = gtk.Dialog("Note Summary", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

        label = gtk.Label("Note Summary:")
        dialog.vbox.pack_start(label, True, True, 0)
        label.show()
        entry = gtk.Entry()
        dialog.vbox.pack_start(entry, True, True, 0)
        entry.show()

        result = dialog.run()
        summary = self.smart_truncate(entry.get_text(), 255, "")
        dialog.destroy()

        if selCat != None:
            cat = selCat.get_label()

        if len(Note.query.filter_by(summary=summary).all()):
            return

        if result == gtk.RESPONSE_ACCEPT:
            n = Note()
            n.summary = summary
            Category.get_category(cat).notes.append(n)
            model.persist()
            n.content = ''

            self.open_note(n)

    def open_note(self, note):
        #page_name = gtk.Label("Note " + str(self.__note_window__.get_n_pages() + 1))
        name = self.smart_truncate(note.summary, 25)
        page_name = gtk.Label(name)
        page_name.show()
        window = gtk.ScrolledWindow()
        window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        buffer = NoteBuffer()
        buffer.note_id = note.id
        textview = gtk.TextView(buffer)
        textview.set_wrap_mode(gtk.WRAP_WORD)
        textview.add_events(gtk.gdk.BUTTON_PRESS_MASK)
        textview.connect_after('populate-popup', self.populate_popup_for_links)
        # textview.connect('button_press_event', self.on_button_press)
        window.add(textview)
        window.show()
        textview.show()
        pid = self.__note_window__.append_page(window, page_name)
        self.__page_note_map__[pid] = note

        tag = buffer.get_tag_table().lookup('link')
        if tag is not None :
            buffer.link_tag = tag
        else :
            buffer.recreate_link()

        buffer.link_tag.connect('event', ANTS.link_handler)

        if note.content is None or len(note.content) == 0:
            return

        # Clear out our created tags
        buffer.wipe_tags()

        serialized = note.content.decode('string_escape')
        buffer = textview.get_buffer()
        start, end = buffer.get_bounds()
        deserialize = buffer.register_deserialize_tagset()
        # Suppress warning if the buffer is empty
        if len(serialized) > 0 :
            buffer.deserialize_set_can_create_tags('application/x-gtk-text-buffer-rich-text', True)
            buffer.deserialize(buffer, deserialize, start, serialized)

        # Fix tags
        tag = buffer.get_tag_table().lookup('Underline')
        if tag is not None :
            buffer.underline_tag = tag
        else :
            buffer.recreate_underline()

        tag = buffer.get_tag_table().lookup('Bold')
        if tag is not None :
            buffer.bold_tag = tag
        else :
            buffer.recreate_bold()

        tag = buffer.get_tag_table().lookup('Italic')
        if tag is not None :
            buffer.italic_tag = tag
        else :
            buffer.recreate_italic()

        tag = buffer.get_tag_table().lookup('Font')
        if tag is not None :
            buffer.font_tag = tag
        else :
            buffer.recreate_font()

        tag = buffer.get_tag_table().lookup('link')
        if tag is not None :
            buffer.link_tag = tag
        else :
            buffer.recreate_link()

        buffer.link_tag.connect('event', ANTS.link_handler)

    def del_note(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            return

        ques = gtk.MessageDialog(self.__wnd__, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO)
        ques.set_title("Delete note?")
        ques.set_markup("Are you sure you want to delete this note?")
        result = ques.run()
        ques.destroy()

        if result == gtk.RESPONSE_YES:
            p = self.__note_window__.get_current_page()

            n = self.__page_note_map__[p]
            self.close_note(n)
            n.delete()
            model.persist()
        elif result == gtk.RESPONSE_NO:
            print "No was clicked"

    def close_note(self, note):
        idx = -1

        for t in self.__page_note_map__:
            if self.__page_note_map__[t].id == note.id:
                idx = t

        self.__note_window__.remove_page(idx)

    def new_category(self, widget):
        dialog = gtk.Dialog("Create Category", self.__wnd__, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))

        label = gtk.Label("Category Name:")
        dialog.vbox.pack_start(label, True, True, 0)
        label.show()
        entry = gtk.Entry()
        dialog.vbox.pack_start(entry, True, True, 0)
        entry.show()

        result = dialog.run()
        name = self.smart_truncate(entry.get_text(), 255, "")
        dialog.destroy()

        if len(Category.query.filter_by(name=name).all()) > 0:
            return

        if result == gtk.RESPONSE_ACCEPT:
            cat = Category()
            cat.name = name
            model.persist()
            self.add_category_button(name)

    def add_category_button(self, name):
        btn = gtk.ToggleButton(label=name)
        btn.connect('toggled', self.category_toggled)
        btn.set_alignment(0.0, 0.0)
        btn.show()
        self.__category_list__.pack_start(btn, False, False, 0)

    def category_toggled(self, path):
        if self.get_selected_category() != None:
            self.__calendar__.select_day(0)

        if self.get_selected_category() == None and self.__calendar__.get_date()[2] == 0:
            path.set_active(True)
        elif self.__calendar__.get_date()[2] != 0:
            return

        # Are we just switching categories?
        if self.get_selected_category() != None and not path.get_active():
            return

        # De-activate all buttons but the one we just clicked
        for tb in self.__category_list__.get_children():
            if tb != path:
                tb.set_active(False)
            else:
                cat = Category.get_category(tb.get_label())

        self.close_all_notes()

        # Populate the notes list with this categories notes
        for n in cat.notes:
            self.open_note(n)

    def close_all_notes(self):
        for i in range(self.__note_window__.get_n_pages(), -1, -1):
            self.__note_window__.remove_page(i)

    def del_category(self, widget):
        cat = self.get_selected_category()

        if cat != None and cat.get_label() != "Unfiled":
            self.close_all_notes()
            Category.get_category(cat.get_label()).delete()
            model.persist()
            self.__category_list__.remove(cat)

    def get_selected_category(self):
        for tb in self.__category_list__.get_children():
            if tb.get_active() == True:
                return tb

        return None

    def day_selected(self, widget):
        # TODO: Clear notes list and populate with notes from selected date
        if widget.get_date()[2] == 0:
            pass
        else:
            cat = self.get_selected_category()
            if cat != None:
                cat.set_active(False)

            self.close_all_notes()

            for n in Note.get_notes_by_date(widget.get_date()):
                self.open_note(n)

    def prev_week(self, widget):
        print "Goto the previous week"

    def prev_day(self, widget):
        print "Goto the previous day"

    def next_week(self, widget):
        print "Goto the next week"

    def next_day(self, widget):
        print "Goto the next day"

    def save_db(self, widget):
        save = gtk.FileChooserDialog("Save ANTS database",
                                     self.__wnd__,
                                     gtk.FILE_CHOOSER_ACTION_SAVE,
                                     (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                      gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        save.set_do_overwrite_confirmation(True)
        filter = gtk.FileFilter()
        filter.set_name("ANTS Database")
        filter.add_pattern("*.ants")
        save.add_filter(filter)

        ret = save.run()
        if ret == gtk.RESPONSE_OK:
            filename = save.get_filename()
            if filename.endswith(".ants") == False:
                filename = filename + ".ants"

            # Put save code here
            print filename

        save.destroy()

    def quicksave_db(self, widget):
        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        start, end = buffer.get_bounds()

        serialized = buffer.serialize(buffer, 'application/x-gtk-text-buffer-rich-text', start, end)
        self.__page_note_map__[p].content = serialized.encode('string_escape')

        model.persist()

    def quicksave_note(self, page_num):
        buffer = self.__note_window__.get_nth_page(page_num).get_children()[0].get_buffer()
        start, end = buffer.get_bounds()

        serialized = buffer.serialize(buffer, 'application/x-gtk-text-buffer-rich-text', start, end)
        self.__page_note_map__[page_num].content = serialized.encode('string_escape')

        model.persist()

    def open_db(self, widget):
        open = gtk.FileChooserDialog("Open ANTS database",
                                     self.__wnd__,
                                     gtk.FILE_CHOOSER_ACTION_OPEN,
                                     (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                      gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        filter = gtk.FileFilter()
        filter.set_name("ANTS Database")
        filter.add_pattern("*.ants")
        open.add_filter(filter)

        ret = open.run()
        if ret == gtk.RESPONSE_OK:
            # Put open code here
            print open.get_filename()

        open.destroy()

    def print_note(self, widget):
        print "Print the current note"

    def italic(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            self.italic_button.set_active(False)
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        buffer.toggleItalics()

        if buffer.get_selection_bounds() != () :
            self.italic_button.handler_block_by_func(self.italic)
            self.italic_button.set_active(not self.italic_button.get_active())
            self.italic_button.handler_unblock_by_func(self.italic)


    def bold(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            self.bold_button.set_active(False)
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        buffer.toggleBold()

        if buffer.get_selection_bounds() != () :
            self.bold_button.handler_block_by_func(self.bold)
            self.bold_button.set_active(not self.bold_button.get_active())
            self.bold_button.handler_unblock_by_func(self.bold)

    def underline(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            self.underline_button.set_active(False)
            return

        p = self.__note_window__.get_current_page()
        buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
        buffer.toggleUnderline()

        if buffer.get_selection_bounds() != () :
            self.underline_button.handler_block_by_func(self.underline)
            self.underline_button.set_active(not self.underline_button.get_active())
            self.underline_button.handler_unblock_by_func(self.underline)

    def bg_color(self, widget):
        cdia = gtk.ColorSelectionDialog("Select background color")
        ret = cdia.run()

        if ret == gtk.RESPONSE_OK:
            colorsel = cdia.colorsel
            color = colorsel.get_current_color()
            print color

        cdia.destroy()

    def fg_color(self, widget):
        cdia = gtk.ColorSelectionDialog("Select foreground color")
        ret = cdia.run()

        if ret == gtk.RESPONSE_OK:
            colorsel = cdia.colorsel
            color = colorsel.get_current_color()
            print color

        cdia.destroy()

    def font_settings(self, widget):
        font = gtk.FontSelectionDialog("Font Settings");
        box_color = gtk.HBox(True, 0)
        box_color.show()
        bnt_bg = gtk.Button("Set background color")
        bnt_bg.connect("clicked", self.bg_color)
        bnt_bg.show()
        box_color.add(bnt_bg)
        bnt_fg = gtk.Button("Set foreground color")
        bnt_fg.connect("clicked", self.fg_color)
        bnt_fg.show()
        box_color.add(bnt_fg)
        font.get_content_area().add(box_color) 
        font_name = "Sans 10"
        if (self.__note_window__.get_n_pages() > 0):
            p = self.__note_window__.get_current_page()
            buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
            font_name = buffer.font_tag.get_property('font')
        font.set_font_name(font_name)
        response = font.run()

        if response == gtk.RESPONSE_OK:
            # Set the font for highlighted and future text here
            if(self.__note_window__.get_n_pages() == 0):
                return

            p = self.__note_window__.get_current_page()
            buffer = self.__note_window__.get_nth_page(p).get_children()[0].get_buffer()
            buffer.font_change(font.get_font_name())

        font.destroy()

    def find_replace(self, widget):
        self.__sw__.show()

    def spell_check(self, widget):
        if(self.__note_window__.get_n_pages() == 0):
            return

        p = self.__note_window__.get_current_page()
        text_view = self.__note_window__.get_nth_page(p).get_children()[0]
        try:
            spell = gtkspell.get_from_text_view(text_view)

            if spell is None:
                spell = gtkspell.Spell(text_view)
            else:
                spell.detach()
        except SystemError: # Its set to none
            spell = gtkspell.Spell(text_view)
            pass

    def __gen_toolbutton__(self, toolbar, icon = None, tooltip = None, connect = None, toggle = None):
        if icon is not None and tooltip is not None and connect is not None:
            if toggle is not None :
                toolbutton = gtk.ToggleToolButton(icon)
            else :
                toolbutton = gtk.ToolButton(icon)
            toolbutton.set_tooltip_text(tooltip)
            toolbutton.connect("clicked", connect)
        else:
            toolbutton = gtk.SeparatorToolItem()

        toolbutton.show()
        toolbar.insert(toolbutton, -1)

        return toolbutton

    def populate_popup_for_links(self, view, menu) :
        sep = gtk.SeparatorMenuItem()
        sep.show()
        menu.append(sep)

        link_site = gtk.MenuItem("Link to _site")
        link_site.show()
        link_site.connect('activate', self.link_site)
        menu.append(link_site)

        link_wiki = gtk.MenuItem("Link to _wikipedia")
        link_wiki.show()
        link_wiki.connect('activate', self.link_wikipedia)
        menu.append(link_wiki)

        link_dict = gtk.MenuItem("Link to _dictionary")
        link_dict.show()
        link_dict.connect('activate', self.link_dictionary)
        menu.append(link_dict)

        remove_link = gtk.MenuItem("_Remove link")
        remove_link.show()
        remove_link.connect('activate', self.remove_link)
        menu.append(remove_link)

        sep = gtk.SeparatorMenuItem()
        sep.show()
        menu.append(sep)

        refresh_links = gtk.MenuItem("Refresh _link")
        refresh_links.show()
        refresh_links.connect('activate', self.refresh_links)
        menu.append(refresh_links)

    def __init__(self):
        # Create the window
        self.__wnd__.set_title("Advanced Note Taking System")
        #TODO: Store this information somewhere so upon reopening the size and
        # window location are always the same
        self.__wnd__.set_size_request(800, 600)
        self.__wnd__.set_position(gtk.WIN_POS_CENTER)
        self.__wnd__.connect("delete_event", self.delete_event)
        self.__wnd__.connect("destroy", self.destroy)

        # Create menus
        menu_bar = gtk.MenuBar()

        # Setup the file menu
        file_menu = gtk.Menu()

        file_menu_item = gtk.MenuItem("_File")
        file_menu_item.show()
        file_menu_item.set_submenu(file_menu)

        # New submenu
        new_menu = gtk.Menu()
        new_category_menu_item = gtk.MenuItem("New _Category")
        new_note_menu_item = gtk.MenuItem("New N_ote")
        new_category_menu_item.show()
        new_note_menu_item.show()
        new_category_menu_item.connect("activate", self.new_category)
        new_note_menu_item.connect("activate", self.new_note)
        new_menu.append(new_category_menu_item)
        new_menu.append(new_note_menu_item)
        new_menu_item = gtk.MenuItem("_New")
        new_menu_item.set_submenu(new_menu)
        new_menu_item.show()
        file_menu.append(new_menu_item)

        open_menu_item = gtk.MenuItem("_Open")
        open_menu_item.show()
        open_menu_item.connect("activate", self.open_db)
        file_menu.append(open_menu_item)

        sep1 = gtk.SeparatorMenuItem()
        sep1.show()
        file_menu.append(sep1)

        save_menu_item = gtk.MenuItem("_Save")
        save_menu_item.show()
        save_menu_item.connect("activate", self.quicksave_db)
        file_menu.append(save_menu_item)
        save_as_menu_item = gtk.MenuItem("Save _As")
        save_as_menu_item.show()
        save_as_menu_item.connect("activate", self.save_db)
        file_menu.append(save_as_menu_item)

        sep2 = gtk.SeparatorMenuItem()
        sep2.show()
        file_menu.append(sep2)

        print_menu_item = gtk.MenuItem("_Print")
        print_menu_item.show()
        print_menu_item.connect("activate", self.print_note)
        file_menu.append(print_menu_item)

        sep3 = gtk.SeparatorMenuItem()
        sep3.show()
        file_menu.append(sep3)

        import_note_menu_item = gtk.MenuItem("_Import Note")
        import_note_menu_item.show()
        import_note_menu_item.connect("activate", self.import_note)
        file_menu.append(import_note_menu_item)
        export_note_menu_item = gtk.MenuItem("_Export Note")
        export_note_menu_item.show()
        export_note_menu_item.connect("activate", self.export_note)
        file_menu.append(export_note_menu_item)

        sep4 = gtk.SeparatorMenuItem()
        sep4.show()
        file_menu.append(sep4)

        quit_menu_item = gtk.MenuItem("_Quit")
        quit_menu_item.show()
        quit_menu_item.connect("activate", self.destroy)
        file_menu.append(quit_menu_item)

        menu_bar.append(file_menu_item)

        # Edit menu
        edit_menu_item = gtk.MenuItem("_Edit")
        edit_menu_item.show()
        edit_menu_item.set_submenu(self.__edit_menu__)

        # Standard text editing options
        cut_menu = gtk.MenuItem("C_ut")
        cut_menu.show()
        cut_menu.connect("activate", self.cut_selected_text)
        self.__edit_menu__.append(cut_menu)
        copy_menu = gtk.MenuItem("C_opy")
        copy_menu.show()
        copy_menu.connect("activate", self.copy_selected_text)
        self.__edit_menu__.append(copy_menu)
        paste_menu = gtk.MenuItem("_Paste")
        paste_menu.show()
        paste_menu.connect("activate", self.paste_text)
        self.__edit_menu__.append(paste_menu)
        delete_menu = gtk.MenuItem("_Delete")
        delete_menu.show()
        delete_menu.connect("activate", self.delete_selected_text)
        self.__edit_menu__.append(delete_menu)

        sep5 = gtk.SeparatorMenuItem()
        sep5.show()
        self.__edit_menu__.append(sep5)

        select_all_menu = gtk.MenuItem("_Select All")
        select_all_menu.show()
        select_all_menu.connect("activate", self.select_all)
        self.__edit_menu__.append(select_all_menu)

        # Linking options
        self.populate_popup_for_links(None, self.__edit_menu__)

        menu_bar.append(edit_menu_item)

        # Tool menu
        tool_menu = gtk.Menu()
        tool_menu_item = gtk.MenuItem("_Tools")
        tool_menu_item.show()
        tool_menu_item.set_submenu(tool_menu)

        # Find options
        find_menu = gtk.MenuItem("_Find and replace")
        find_menu.show()
        find_menu.connect("activate", self.find_replace)
        tool_menu.append(find_menu)

        sep6 = gtk.SeparatorMenuItem()
        sep6.show()
        tool_menu.append(sep6)

        # Spell check
        spell_menu = gtk.MenuItem("_Spell check")
        spell_menu.show()
        spell_menu.connect("activate", self.spell_check)
        tool_menu.append(spell_menu)

        menu_bar.append(tool_menu_item)

        # Help menu
        help_menu = gtk.Menu()
        help_menu_item = gtk.MenuItem("_Help")
        help_menu_item.show()
        help_menu_item.set_submenu(help_menu)

        about_menu_item = gtk.MenuItem("_About")
        about_menu_item.show()
        about_menu_item.connect("activate", self.about)
        help_menu.append(about_menu_item)

        menu_bar.append(help_menu_item)
        menu_bar.show()

        # Create the first container which holds the main menu and the main
        # horizontal pane which contains everything else
        menu_box = gtk.VBox(False, 0)
        self.__wnd__.add(menu_box)
        menu_box.pack_start(menu_bar, False, False, 0)
        # The second container which holds the calendar, categorys, and notes
        main_pane = gtk.HPaned()
        menu_box.pack_end(main_pane, True, True, 0)

        # The left pane which contains the calendar and categorys
        left_box = gtk.VPaned()
        main_pane.add1(left_box)
        self.__calendar__ = gtk.Calendar()
        self.__calendar__.connect("day_selected", self.day_selected)
        self.__calendar__.select_day(0)
        left_box.add1(self.__calendar__)
        self.__calendar__.show()

        # The category box which contains everything for adding removing and
        # select categorys
        category_box = gtk.VBox(False, 0)
        category_label = gtk.Label("Categories")
        category_label.show()
        category_box.pack_start(category_label, False, False, 0)

        # Setup the category toolbar for adding and deleting notes
        # Also has options to seek the calendar by week or day
        category_toolbar = gtk.Toolbar()
        category_toolbar.set_style(gtk.TOOLBAR_ICONS)
        self.__gen_toolbutton__(category_toolbar, gtk.STOCK_NEW, "New Category", self.new_category)
        self.__gen_toolbutton__(category_toolbar, gtk.STOCK_DELETE, "Delete Category", self.del_category)
        self.__gen_toolbutton__(category_toolbar)
        #self.__gen_toolbutton__(category_toolbar, gtk.STOCK_GOTO_FIRST, "Previous Week", self.prev_week)
        #self.__gen_toolbutton__(category_toolbar, gtk.STOCK_GO_BACK, "Previous Day", self.prev_day)
        #self.__gen_toolbutton__(category_toolbar, gtk.STOCK_GO_FORWARD, "Next Day", self.next_day)
        #self.__gen_toolbutton__(category_toolbar, gtk.STOCK_GOTO_LAST, "Next Week", self.next_week)
        category_toolbar.show()
        category_box.pack_start(category_toolbar, False, False, 0)

        # The scrolled window contains all the category buttons
        self.__category_window__.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

        for cat in Category.query.all():
            self.add_category_button(cat.name)

        self.__category_list__.get_children()[0].set_active(True)

        self.__category_list__.show()
        self.__category_window__.add_with_viewport(self.__category_list__)
        self.__category_window__.show()
        category_box.pack_end(self.__category_window__, True, True, 0)

        left_box.add2(category_box)

        left_box.add2(category_box)
        category_box.show()
        left_box.show()

        # The right side of the window which contains the editing toolbar
        # and the notes themselves
        right_box = gtk.VBox(False, 0)

        # The note toolbar which contains saving, font settings, etc
        note_toolbar = gtk.Toolbar()
        note_toolbar.set_style(gtk.TOOLBAR_ICONS)
        self.__gen_toolbutton__(note_toolbar, gtk.STOCK_SAVE, "Quick Save", self.quicksave_db)
        self.__gen_toolbutton__(note_toolbar, gtk.STOCK_OPEN, "Open", self.open_db)
        self.__gen_toolbutton__(note_toolbar, gtk.STOCK_PRINT, "Print Note", self.print_note)
        self.__gen_toolbutton__(note_toolbar)
        self.__gen_toolbutton__(note_toolbar, gtk.STOCK_NEW, "New Note", self.new_note)
        self.__gen_toolbutton__(note_toolbar, gtk.STOCK_DELETE, "Delete Note", self.del_note)
        self.__gen_toolbutton__(note_toolbar)
        self.italic_button = self.__gen_toolbutton__(note_toolbar, gtk.STOCK_ITALIC, "Italics", self.italic, True)
        self.bold_button = self.__gen_toolbutton__(note_toolbar, gtk.STOCK_BOLD, "Bold", self.bold, True)
        self.underline_button = self.__gen_toolbutton__(note_toolbar, gtk.STOCK_UNDERLINE, "Underline", self.underline, True)
        self.__gen_toolbutton__(note_toolbar)
        self.__gen_toolbutton__(note_toolbar, gtk.STOCK_SELECT_FONT, "Font Settings", self.font_settings)
        self.__gen_toolbutton__(note_toolbar)
        self.__gen_toolbutton__(note_toolbar, gtk.STOCK_SPELL_CHECK, "Spell Check", self.spell_check, True)
        self.__gen_toolbutton__(note_toolbar, gtk.STOCK_FIND_AND_REPLACE, "Find and Replace", self.find_replace)
        note_toolbar.show()
        right_box.pack_start(note_toolbar, False, False, 0)

        # Setup the notebook which contains all notes
        self.__note_window__.set_tab_pos(gtk.POS_TOP)
        self.__note_window__.set_show_tabs(True)
        self.__note_window__.set_scrollable(True)
        self.__note_window__.show()
        right_box.pack_end(self.__note_window__, True, True, 0)
        right_box.show()
        main_pane.add2(right_box)

        main_pane.show()
        menu_box.show()
        self.__wnd__.show()

    def smart_truncate(self, content, length=100, suffix='...'):
        if len(content) <= length:
            return content
        else:
            return ' '.join(content[:length+1].split(' ')[0:-1]) + suffix

if __name__ == "__main__":
    model.initialize_and_upgrade()

    ants = ANTS()
    gtk.main()

